home *** CD-ROM | disk | FTP | other *** search
- /*
- * MUSCROLL.C
- *
- * Contains the main window procedure of the MicroScroll control
- * that handles mouse logic, and Windows messages.
- *
- * Version 1.1, October 1991, Kraig Brockschmidt
- */
-
-
- #include <windows.h>
- #include "muscrdll.h"
-
-
-
- /*
- * MicroScrollWndProc
- *
- * Purpose:
- * Window Procedure for the MicroScroll custom control. Handles all
- * messages like WM_PAINT just as a normal application window would.
- * Any message not processed here should go to DefWindowProc.
- *
- * Parameters:
- * hWnd HWND handle to the control window.
- * iMsg WORD message identifier.
- * wParam WORD parameter of the message.
- * lParam LONG parameter of the message.
- *
- * Return Value:
- * LONG Varies with the message.
- *
- */
-
- LONG FAR PASCAL MicroScrollWndProc(HWND hWnd, WORD iMsg,
- WORD wParam, LONG lParam)
- {
- PMUSCROLL pMS;
- POINT pt;
- RECT rect;
- short x, y;
- short cx, cy;
- WORD wState;
-
-
- /*
- * Get a pointer to the MUSCROLL structure for this control.
- * Note that if we do this before WM_NCCREATE where we allocate
- * the memory, pMS will be NULL, which is not a problem since
- * we do not access it until after WM_NCCREATE.
- */
- pMS=(PMUSCROLL)GetWindowWord(hWnd, GWW_MUSCROLLHMEM);
-
-
- //Let the API handler process WM_USER+xxxx messages
- if (iMsg >= WM_USER)
- return LMicroScrollAPI(hWnd, iMsg, wParam, lParam, pMS);
-
-
- //Handle standard Windows messages.
- switch (iMsg)
- {
- case WM_NCCREATE:
- case WM_CREATE:
- return LMicroScrollCreate(hWnd, iMsg, pMS, (LPCREATESTRUCT)lParam);
-
- case WM_NCDESTROY:
- //Free the control's memory.
- LocalFree((HANDLE)pMS);
- break;
-
- case WM_ERASEBKGND:
- /*
- * Eat this message to avoid erasing portions that
- * we are going to repaint in WM_PAINT. Part of a
- * change-state-and-repaint strategy is to rely on
- * WM_PAINT to do anything visual, which includes
- * erasing invalid portions. Letting WM_ERASEBKGND
- * erase the background is redundant.
- */
- break;
-
-
- case WM_PAINT:
- return LMicroScrollPaint(hWnd, pMS);
-
-
- case WM_ENABLE:
- /*
- * Handles disabling/enabling case. Example of a
- * change-state-and-repaint strategy since we let the
- * painting code take care of the visuals.
- */
- if (wParam)
- StateClear(pMS, MUSTATE_GRAYED);
- else
- StateSet(pMS, MUSTATE_GRAYED);
-
- //Force a repaint since the control will look different.
- InvalidateRect(hWnd, NULL, TRUE);
- UpdateWindow(hWnd);
- break;
-
-
- case WM_SHOWWINDOW:
- /*
- * Set or clear the hidden flag. Windows will
- * automatically force a repaint if we become visible.
- */
- if (wParam)
- StateClear(pMS, MUSTATE_HIDDEN);
- else
- StateSet(pMS, MUSTATE_HIDDEN);
-
- break;
-
-
- case WM_CANCELMODE:
- /*
- * IMPORTANT MESSAGE! WM_CANCELMODE means that a
- * dialog or some other modal process has started.
- * we must make sure that we cancel any clicked state
- * we are in, kill the timers, and release the capture.
- */
- StateClear(pMS, MUSTATE_DOWNCLICK | MUSTATE_UPCLICK);
- KillTimer(hWnd, IDT_FIRSTCLICK);
- KillTimer(hWnd, IDT_HOLDCLICK);
- ReleaseCapture();
- break;
-
-
- case WM_TIMER:
- /*
- * We run two timers: the first is the initial delay
- * after the first click before we begin repeating, the
- * second is the repeat rate.
- */
- if (wParam==IDT_FIRSTCLICK)
- {
- KillTimer(hWnd, wParam);
- SetTimer(hWnd, IDT_HOLDCLICK, CTICKS_HOLDCLICK, NULL);
- }
-
- /*
- * Send a new scroll message if the mouse is still in the
- * originally clicked area.
- */
- if (!StateTest(pMS, MUSTATE_MOUSEOUT))
- PositionChange(hWnd, pMS);
-
- break;
-
-
- case WM_LBUTTONDBLCLK:
- case WM_LBUTTONDOWN:
- /*
- * When we get a mouse down message, we know that the mouse
- * is over the control. We then do the following steps
- * to set up the new state:
- * 1. Hit-test the coordinates of the click to
- * determine in which half the click occurred.
- * 2. Set the appropriate MUSTATE_*CLICK state
- * and repaint that clicked half. This is another
- * example of a change-state-and-repaint strategy.
- * 3. Send an initial scroll message.
- * 4. Set the mouse capture.
- * 5. Set the initial delay timer before repeating
- * the scroll message.
- *
- * A WM_LBUTTONDBLCLK message means that the user clicked
- * the control twice in succession which we want to treat
- * like WM_LBUTTONDOWN. This is safe since we will receive
- * WM_LBUTTONUP before the WM_LBUTTONDBLCLK.
- */
-
- //Get the mouse coordinates.
- x=LOWORD(lParam);
- y=HIWORD(lParam);
-
-
- /*
- * Only need to hit-test the upper half for a vertical
- * control or the left half for a horizontal control.
- */
- GetClientRect(hWnd, &rect);
- cx=rect.right >> 1;
- cy=rect.bottom >> 1;
-
- if (MSS_VERTICAL & pMS->dwStyle)
- wState=(y > cy) ? MUSTATE_DOWNCLICK : MUSTATE_UPCLICK;
- else
- wState=(x > cx) ? MUSTATE_RIGHTCLICK : MUSTATE_LEFTCLICK;
-
- //Change-state-and-repaint
- StateSet(pMS, wState);
- ClickedRectCalc(hWnd, pMS, &rect);
- InvalidateRect(hWnd, &rect, TRUE);
- UpdateWindow(hWnd);
-
- PositionChange(hWnd, pMS);
- SetCapture(hWnd);
- SetTimer(hWnd, IDT_FIRSTCLICK, CTICKS_FIRSTCLICK, NULL);
- break;
-
-
- case WM_MOUSEMOVE:
- /*
- * On WM_MOUSEMOVE messages we want to know if the mouse
- * has moved out of the control when the control is in
- * a clicked state. If the control has not been clicked,
- * then we have nothing to do. Otherwise we want to set
- * the MUSTATE_MOUSEOUT flag and repaint so the button
- * visually comes up.
- */
- if (!StateTest(pMS, MUSTATE_CLICKED))
- break;
-
-
- //Get the area we originally clicked and the new POINT
- ClickedRectCalc(hWnd, pMS, &rect);
- pt=MAKEPOINT(lParam);
-
- wState=pMS->wState;
-
- //Hit-Test the rectange and change the state if necessary.
- if (PtInRect(&rect, pt))
- StateClear(pMS, MUSTATE_MOUSEOUT);
- else
- StateSet(pMS, MUSTATE_MOUSEOUT);
-
- /*
- * If the state changed, repaint the appropriate part of
- * the control.
- */
- if (wState!=pMS->wState)
- {
- InvalidateRect(hWnd, &rect, TRUE);
- UpdateWindow(hWnd);
- }
-
- break;
-
-
- case WM_LBUTTONUP:
- /*
- * A mouse button up event is much like WM_CANCELMODE since
- * we have to clean out whatever state the control is in:
- * 1. Kill any repeat timers we might have created.
- * 2. Release the mouse capture.
- * 3. Clear the clicked states and repaint, another example
- * of a change-state-and-repaint strategy.
- */
- KillTimer(hWnd, IDT_FIRSTCLICK);
- KillTimer(hWnd, IDT_HOLDCLICK);
-
- ReleaseCapture();
-
-
-
- /*
- * Repaint if necessary, only if we are clicked AND the mouse
- * is still in the boundaries of the control.
- */
- if (StateTest(pMS, MUSTATE_CLICKED) &&
- StateTest(pMS, ~MUSTATE_MOUSEOUT))
- {
- //Calculate the rectangle before clearing states.
- ClickedRectCalc(hWnd, pMS, &rect);
-
- //Clear the states so we repaint properly.
- StateClear(pMS, MUSTATE_MOUSEOUT);
- StateClear(pMS, MUSTATE_CLICKED);
-
-
- InvalidateRect(hWnd, &rect, TRUE);
- UpdateWindow(hWnd);
- }
-
- //Insure that we clear out the states.
- break;
-
-
- default:
- return DefWindowProc(hWnd, iMsg, wParam, lParam);
- }
-
- return 0L;
- }
-
-
-
-
-
-
- /*
- * ClickedRectCalc
- *
- * Purpose:
- * Calculates the rectangle of the clicked region based on the
- * state flags MUSTATE_UPCLICK, MUSTATE_DOWNCLICK, MUSTATE_LEFTCLICK,
- * and MUSTATE_RIGHTLICK, depending on the style.
- *
- * Parameter:
- * hWnd HWND handle to the control window.
- * lpRect LPRECT rectangle structure to fill.
- *
- * Return Value:
- * void
- *
- */
-
- void PASCAL ClickedRectCalc(HWND hWnd, PMUSCROLL pMS, LPRECT lpRect)
- {
- short cx, cy;
-
- GetClientRect(hWnd, lpRect);
- cx=lpRect->right >> 1;
- cy=lpRect->bottom >> 1;
-
- if (MSS_VERTICAL & pMS->dwStyle)
- {
- if (StateTest(pMS, MUSTATE_DOWNCLICK))
- lpRect->top=cy;
-
- if (StateTest(pMS, MUSTATE_UPCLICK))
- lpRect->bottom=1+cy;
- }
- else
- {
- //MSS_HORIZONTAL
- if (StateTest(pMS, MUSTATE_RIGHTCLICK))
- lpRect->left=cx;
-
- if (StateTest(pMS, MUSTATE_LEFTCLICK))
- lpRect->right=1+cx;
- }
-
- return;
- }
-
-
-
-
-
- /*
- * PositionChange
- *
- * Purpose:
- * Checks what part of the control is clicked, modifies the current
- * position accordingly (taking MSS_INVERTRANGE into account) and
- * sends an appropriate message to the associate. For MSS_VERTICAL
- * controls we send WM_VSCROLL messages and for MSS_HORIZONTAL controls
- * we send WM_HSCROLL.
- *
- * The scroll code in the message is always SB_LINEUP for the upper
- * or left half of the control (vertical and horizontal, respectively)
- * and SB_LINEDOWN for the bottom or right half.
- *
- * This function does not send a message if the position is pegged
- * on the minimum or maximum of the range if MSS_NOPEGSCROLL is
- * set in the style bits.
- *
- * Parameters:
- * hWnd HWND of the control.
- * pMS PMUSCROLL pointer to control data structure.
- *
- * Return Value:
- * void
- */
-
- void PASCAL PositionChange(HWND hWnd, PMUSCROLL pMS)
- {
- WORD wScrollMsg;
- WORD wScrollCode;
- BOOL fPegged=FALSE;
-
- if (StateTest(pMS, MUSTATE_UPCLICK | MUSTATE_LEFTCLICK))
- wScrollCode=SB_LINEUP;
-
- if (StateTest(pMS, MUSTATE_DOWNCLICK | MUSTATE_RIGHTCLICK))
- wScrollCode=SB_LINEDOWN;
-
- wScrollMsg=(MSS_VERTICAL & pMS->dwStyle) ? WM_VSCROLL : WM_HSCROLL;
-
- /*
- * Modify the current position according to the following rules:
- *
- * 1. On SB_LINEUP with an inverted range, increment the position.
- * If the position is already at the maximum, set the pegged flag.
- *
- * 2. On SB_LINEUP with an normal range, decrement the position.
- * If the position is already at the minimum, set the pegged flag.
- *
- * 3. On SB_LINEDOWN with an inverted range, treat like SB_LINEUP
- * with a normal range.
- *
- * 4. On SB_LINEDOWN with an normal range, treat like SB_LINEUP
- * with an inverted range.
- */
-
- if (wScrollCode==SB_LINEUP)
- {
- if (MSS_INVERTRANGE & pMS->dwStyle)
- {
- if (pMS->iPos==pMS->iMax)
- fPegged=TRUE;
- else
- pMS->iPos++;
- }
- else
- {
- if (pMS->iPos==pMS->iMin)
- fPegged=TRUE;
- else
- pMS->iPos--;
- }
- }
- else
- {
- if (MSS_INVERTRANGE & pMS->dwStyle)
- {
- if (pMS->iPos==pMS->iMin)
- fPegged=TRUE;
- else
- pMS->iPos--;
- }
- else
- {
- if (pMS->iPos==pMS->iMax)
- fPegged=TRUE;
- else
- pMS->iPos++;
- }
- }
-
-
- /*
- * Send a message if we changed and are not pegged, or did not change
- * and MSS_NOPEGSCROLL is clear.
- */
- if (!fPegged || !(MSS_NOPEGSCROLL & pMS->dwStyle))
- {
- SendMessage(pMS->hWndAssociate, wScrollMsg,
- wScrollCode, MAKELONG(pMS->iPos, hWnd));
- }
-
- return;
- }
-